#include <QtGui>
#include <iostream>
#include <assert.h>

#include "include/mainwindow.h"
#include "include/treemodel.h"
#include "include/Common.h"
#include "include/TextToSpeech.h"
#include "include/PersistentDialog.h"
#include "include/Preferences.h"
#include "include/mucustomshortcuts.h"
#include "include/itreeitemfactory.h"
#include "include/about.h"

#include <QTimer>
#include <QToolBar>
#include <QFileDialog>

using namespace std;


namespace
{
	//const QString docStyleSheet = "html { font-size: 12pt; font-weight: bold; margin: 0; }";

	template <class T>
	struct TreeItemFactoryChanger
	{
			TreeItemFactoryChanger(TreeModel* model)
				: _model(model)
				, _ownFactory(0)
				, _savedFactory(0)
			{
				if (_model == 0)
					return;

				_ownFactory = new T;
				_savedFactory = _model->treeItemFactory();
				_model->setTreeItemFactory(_ownFactory);
			}

			~TreeItemFactoryChanger()
			{
				if (_model == 0)
					return;

				_model->setTreeItemFactory(_savedFactory);
				delete _ownFactory;
			}

		private:

			TreeModel* _model;
			ITreeItemFactory* _ownFactory;
			ITreeItemFactory* _savedFactory;
	};

	struct SiblingItemFactoryChanger : public TreeItemFactoryChanger<SiblingItemFactory>
	{
		SiblingItemFactoryChanger(TreeModel* model) : TreeItemFactoryChanger<SiblingItemFactory>(model) {}
	};

	struct LeafItemFactoryChanger : public TreeItemFactoryChanger<LeafItemFactory>
	{
		LeafItemFactoryChanger(TreeModel* model) : TreeItemFactoryChanger<LeafItemFactory>(model) {}
	};


} //private namespace


MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent),
	  m_currentItemObserver(0),
	  m_muShortcuts(0)
{

	setupUi(this);

	int x, y;
	int screenWidth;
	int screenHeight;

	int WIDTH = 1000;
	int HEIGHT = 600;

	QDesktopWidget *desktop = QApplication::desktop();

	screenWidth = desktop->width();
	screenHeight = desktop->height();

	x = (screenWidth - WIDTH) / 2;
	y = (screenHeight - HEIGHT) / 2;

	this->setGeometry(x, y, WIDTH, HEIGHT);

//	QStringList headers;
//	headers << tr("Question") << tr("Answer");

	TreeModel *model = new TreeModel(this);
	w_view->setModel(model);

	for (int column = 0; column < model->columnCount(); ++column)
		w_view->resizeColumnToContents(column);

	connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));

//	connect(w_view->selectionModel(),
//			SIGNAL(selectionChanged(const QItemSelection &,
//									const QItemSelection &)),
//			this, SLOT(updateActions()));

	connect(w_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(onCurrentChanged(QModelIndex,QModelIndex)) );

	m_currentItemObserver = new CurrentItemObserver(this);

	w_find->hide();

	connect(actionsMenu, SIGNAL(aboutToShow()),					this, SLOT(updateActions()));
	connect(insertRowAction, SIGNAL(triggered()),					this, SLOT(insertRow()));
	connect(removeRowAction, SIGNAL(triggered()),					this, SLOT(removeRow()));
	connect(insertLeafAction, SIGNAL(triggered()),					this, SLOT(insertLeaf()));
	connect(actionEnable_Multi_Selection, SIGNAL(triggered()),		this, SLOT(multiSelectionPressed()));
	connect(actionConvert_Selection_to_Cards, SIGNAL(triggered()),	this, SLOT(convertSelectionToCards()));
	connect(saveAction, SIGNAL( triggered() ),						w_view->model(), SLOT(saveChanges()));
	connect(actionInvert_card, SIGNAL(triggered()),				this, SLOT(invertItem()));
	connect(actionEnter_Persistent_Mode, SIGNAL(triggered()),		this, SLOT(persistentModeClicked()));
	connect(actionPause_Card, SIGNAL(triggered()),				this, SLOT(pauseBranch()));

	w_questionArea->setHorizontalAlignment(Qt::AlignHCenter);
	w_answerArea->setHorizontalAlignment(Qt::AlignHCenter);

	w_questionArea->addAction(actionEditCard);
	w_questionArea->addAction(actionStopEditCard);
	w_answerArea->addAction(actionEditCard);
	w_answerArea->addAction(actionStopEditCard);

	actionEditCard->setShortcutContext(Qt::WidgetShortcut);
	actionStopEditCard->setShortcutContext(Qt::WidgetShortcut);
	connect(actionEditCard, SIGNAL(triggered()), this, SLOT(beginEditFocusedArea()));
	connect(actionStopEditCard, SIGNAL(triggered()), this, SLOT(endEditFocusedArea()));

	//////// DEBUG CONNECTIONS GO HERE /////////
	//////
	connect(actionGet_Due_Date, SIGNAL( triggered() ), this, SLOT(printDueDate()));
	////////////////////////////////////////////

	//connect(w_view, SIGNAL(activated(const QModelIndex &)), this, SLOT(diplayCard(const QModelIndex &)));
	//connect(w_view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(diplayCard(const QModelIndex &)));

	// Keyboard Shortcuts
	QShortcut* pTreeViewDeselectAll = new QShortcut(QKeySequence(tr("ESCAPE")), w_view);
	pTreeViewDeselectAll->setContext(Qt::WidgetShortcut);

	m_shortcuts["cut"] =					new QShortcut(QKeySequence(tr("CTRL+x", "cut")), w_view);
	m_shortcuts["cut"]->setWhatsThis(tr("Cut"));

	m_shortcuts["paste"] =					new QShortcut(QKeySequence(tr("CTRL+v", "paste")), w_view);
	m_shortcuts["paste"]->setWhatsThis(tr("Paste"));

	m_shortcuts["paste into"] =				new QShortcut(QKeySequence(tr("CTRL+SHIFT+v", "paste into")), w_view);
	m_shortcuts["paste into"]->setWhatsThis(tr("Paste Into"));

	m_shortcuts["find"] =					new QShortcut(QKeySequence(tr("CTRL+f", "find")), this);
	m_shortcuts["find"]->setWhatsThis(tr("Find"));

	m_shortcuts["esc"] =					pTreeViewDeselectAll;
	m_shortcuts["esc"]->setWhatsThis(tr("Deselect All Nodes"));

	connect(m_shortcuts["cut"],				SIGNAL(activated()), this,			SLOT(cut()));
	connect(m_shortcuts["paste"],			SIGNAL(activated()), this,			SLOT(paste()));
	connect(m_shortcuts["paste into"],		SIGNAL(activated()), this,			SLOT(pasteInto()));
	connect(m_shortcuts["find"],			SIGNAL(activated()), this,			SLOT(find()));
	connect(m_shortcuts["esc"],				SIGNAL(activated()), this,			SLOT(deselectAll()));


	updateActions();

	m_pUpdateTimer = new QTimer(this);
	m_pUpdateTimer->setInterval(1000);
	m_pUpdateTimer->start();

	connect(m_pUpdateTimer, SIGNAL(timeout()),	w_view, SLOT(update()));

	m_pReviewTimer = new QTimer(this);
	m_pReviewTimer->setInterval(5*60*1000);
	connect(m_pReviewTimer, SIGNAL(timeout()), this, SLOT(enterPersistentMode()));
	m_persistentDialogIsShowing = false;

	const QString imageResourceName = "card://image";
	w_questionArea->setImageResourceName(imageResourceName);
	w_answerArea->setImageResourceName(imageResourceName);
	m_currentItemObserver->setImageResourceName(imageResourceName);

	QFont fnt = this->font();
	fnt.setPointSize(12); //pointSize vs pixelSize, see: http://www.qtforum.org/article/10502/qfont-size.html
	fnt.setBold(true);
	m_documentFont = fnt;
	m_currentItemObserver->setDefaultFont(fnt);

	connect(w_questionArea, SIGNAL(focusOut()), actionStopEditCard, SLOT(trigger()));
	connect(w_answerArea, SIGNAL(focusOut()), actionStopEditCard, SLOT(trigger()));

	m_currentItemObserver->bindTo(model, w_view->selectionModel());
	w_questionArea->setDocument(m_currentItemObserver->question());
	w_answerArea->setDocument(m_currentItemObserver->answer());

	setupShortcuts();
	setupTheme();
}

MainWindow::~MainWindow()
{
	delete m_muShortcuts;
}



void MainWindow::setupShortcuts()
{
	actionSave_As->setShortcut(QKeySequence::SaveAs); //using standard keys
	//actionNew->setShortcut(QKeySequence::New);
	actionOpen->setShortcut(QKeySequence::Open);

	m_muShortcuts = new MuCustomShortcuts();

	m_muShortcuts->add(exitAction);
	m_muShortcuts->add(insertRowAction);
	m_muShortcuts->add(removeRowAction);
	m_muShortcuts->add(insertLeafAction);
	m_muShortcuts->add(saveAction);
	m_muShortcuts->add(actionReview_Group);
	m_muShortcuts->add(actionEnable_Multi_Selection);
	m_muShortcuts->add(actionRandom);
	m_muShortcuts->add(actionStop_Review);
	m_muShortcuts->add(actionConvert_Selection_to_Cards);
	m_muShortcuts->add(actionGet_Due_Date);
	m_muShortcuts->add(actionPause_Card);
	m_muShortcuts->add(actionInvert_card);
	m_muShortcuts->add(actionReview_Branch);
	m_muShortcuts->add(actionEnter_Persistent_Mode);
	m_muShortcuts->add(actionPreferences);
	m_muShortcuts->add(actionImport_Data);
	m_muShortcuts->add(actionEditCard);
	m_muShortcuts->add(actionStopEditCard);
	m_muShortcuts->add(actionSave_As); //uses standard key

	for (QMap<QString, QShortcut*>::const_iterator it=m_shortcuts.begin(); it != m_shortcuts.end(); ++it)
	{
		QShortcut* shk = it.value();
		m_muShortcuts->add(shk);
	}

	m_muShortcuts->sort();

	m_muShortcuts->loadFromSettings();
}


void MainWindow::setupTheme()
{
//	actionNew->setIcon(QIcon::fromTheme("document-new"));
//	actionOpen->setIcon(QIcon::fromTheme("document-open"));
//	actionSave_As->setIcon(QIcon::fromTheme("document-save-as"));
}


void MainWindow::insertLeaf()
{
	TreeModel* model = getTreeModel();
	SiblingItemFactoryChanger changer(model);

	QModelIndex currentIndex = getIndex();
	QModelIndex index = model->index(currentIndex.row(), 0, currentIndex.parent());

	if (!model->insertRow(0, index))
		return;

	w_view->selectionModel()->setCurrentIndex(model->index(0, 0, index),
			QItemSelectionModel::ClearAndSelect);
	updateActions();
}

void MainWindow::insertRow()
{
	TreeModel *model = getTreeModel();
	SiblingItemFactoryChanger changer(model);

	QModelIndex index = getIndex();
	int posToInsert = index.isValid() ? index.row() + 1 : model->rowCount(index);

	if (!model->insertRow(posToInsert, index.parent())) //parent of root is root itself (i.e. invalid index)
		return;

	w_view->selectionModel()->select(getTreeModel()->index(posToInsert, index.column(), index.parent()), QItemSelectionModel::ClearAndSelect);
	w_view->selectionModel()->setCurrentIndex(getTreeModel()->index(posToInsert, index.column(), index.parent()), QItemSelectionModel::ClearAndSelect);

	updateActions();
}

void MainWindow::removeRow()
{
	QModelIndex index = getIndex();
	QAbstractItemModel *model = w_view->model();
	if (model->removeRow(index.row(), index.parent()))
		updateActions();
}

//this function was redundant, printDueDate can be used instead
//void MainWindow::diplayCard(const QModelIndex& index)
//{
//	Q_UNUSED(index);
//	//m_pCardRenderer->displayCard(getTreeModel(), index, CardRenderer::eBOTH);
//	printDueDate();
//}

void MainWindow::updateActions()
{
	QModelIndex index = w_view->selectionModel()->currentIndex();

	//bool hasSelection = !w_view->selectionModel()->selection().isEmpty();
	bool hasCurrent = index.isValid();
	removeRowAction->setEnabled(hasCurrent);
	insertLeafAction->setEnabled(hasCurrent);
	actionConvert_Selection_to_Cards->setEnabled(hasCurrent);
	actionPause_Card->setEnabled(hasCurrent);
	actionInvert_card->setEnabled(hasCurrent);
	actionEditCard->setEnabled(hasCurrent);

	if (hasCurrent)
	{
		w_view->closePersistentEditor(index);
		CardInfo info = getTreeModel()->getCardInfo(index);
		actionPause_Card->setChecked(info.m_treeItem->isPaused());
		actionConvert_Selection_to_Cards->setChecked(info.m_treeItem->isCard());
	}
}

void MainWindow::cut()
{
	QModelIndex index = getIndex();
	if (getTreeModel()->cut(index.row(), 1, index.parent()))
		updateActions();
}

void MainWindow::paste()
{
	QModelIndex index = getIndex();
	if (getTreeModel()->paste(index.row(), 1, index.parent()))
		updateActions();
}

void MainWindow::pasteInto()
{
	QModelIndex index = getIndex();
	if (getTreeModel()->paste(0, 1, index))
		updateActions();
	else
		qDebug() << "pasteInto failed";
}

void MainWindow::multiSelectionPressed()
{
	w_view->setSelectionMode(	actionEnable_Multi_Selection->isChecked() ?
								QAbstractItemView::MultiSelection :
								QAbstractItemView::SingleSelection);
}

void MainWindow::convertSelectionToCards()
{
	getTreeModel()->convertSelectionToCards(getIndex(), actionConvert_Selection_to_Cards->isChecked());
}

void MainWindow::find()
{
	w_find->show();
}

void MainWindow::setShortcutsEnabled(bool b)
{
	for (QMap<QString, QShortcut*>::iterator it = m_shortcuts.begin(); it != m_shortcuts.end(); it++)
		(*it)->setEnabled(b);
}

void MainWindow::pauseBranch()
{
	getTreeModel()->pauseBranch(getIndex(), actionPause_Card->isChecked());
}

void MainWindow::invertItem()
{
	getTreeModel()->invertItem(getIndex());
}

void MainWindow::persistentModeClicked()
{
	if (!actionEnter_Persistent_Mode->isChecked())
	{
		actionEnter_Persistent_Mode->setChecked(actionEnter_Persistent_Mode->isChecked());
		m_pReviewTimer->stop();
	}
	else
	{
		actionEnter_Persistent_Mode->setChecked(actionEnter_Persistent_Mode->isChecked());
		enterPersistentMode();
	}
}

void MainWindow::enterPersistentMode()
{
	if (m_persistentDialogIsShowing)
		return;

	CardInfo info;
	getTreeModel()->getDueCardsDepthFirst(QModelIndex(), info);

	if (info.m_card != 0 && info.m_card->isNull() == false)
	{
		actionEnter_Persistent_Mode->setChecked(true);
		//memory leak is here
		PersistentDialog* pPersistentDialog = new PersistentDialog();
		//pPersistentDialog->setDocStyleSheet(docStyleSheet);
		pPersistentDialog->setDocumentFont(m_documentFont);
		pPersistentDialog->setCardInfo(info);

		connect(pPersistentDialog, SIGNAL(finished(int)), this, SLOT(persistentDialogReturned(int)));
		connect(pPersistentDialog, SIGNAL(rejected()), m_pReviewTimer, SLOT(stop()));
		connect(pPersistentDialog, SIGNAL(answerDisplayed()), this, SLOT(onPersistentDialogAnswerDisplayed()));
		QItemSelectionModel* sel = w_view->selectionModel();
		w_view->hideColumn(1);
		sel->clearSelection();
		QModelIndex idx = getTreeModel()->indexOf(info.m_treeItem);

		sel->setCurrentIndex(idx, QItemSelectionModel::Current);

		w_view->scrollTo(idx, QTreeView::PositionAtCenter);

		w_answerArea->setContentIsVisible(false);

		pPersistentDialog->show();
		m_persistentDialogIsShowing = true;
	}
	else
	{
		w_view->showColumn(1);
	}
}


void MainWindow::onPersistentDialogAnswerDisplayed()
{
	w_answerArea->setContentIsVisible(true);
}


void MainWindow::persistentDialogReturned(int r)
{
	getTreeModel()->saveChanges();
	//getTreeModel()->serialize(); //this method was called without any conditions in PersistentDialog's destructor before

	m_persistentDialogIsShowing = false;
	w_answerArea->setContentIsVisible(true);

	if (r == QDialog::Rejected)
	{
		actionEnter_Persistent_Mode->setChecked(false);
		this->setFocus();
		showAnswerColumn();
	}
	if (r == QDialog::Accepted)
	{
		// are there due cards?
/*		bool hasDueCards = getTreeModel()->hasDueCards();
		// if so continue reviewing
		if (hasDueCards)
		{
			m_pReviewTimer->stop();
			//getTreeModel()->serialize();
			enterPersistentMode();
			return;
		}
		else*/
		{
			showAnswerColumn();
			// else start timer
			m_pReviewTimer->start();
		}
	}
}

void MainWindow::showAnswerColumn()
{
	w_view->showColumn(1);
	QRect rect = w_view->geometry();
	w_view->setColumnWidth(0, rect.width()/2);
}

void MainWindow::deselectAll()
{
	w_view->selectionModel()->clear();
	w_view->setCurrentIndex(QModelIndex());
	//diplayCard(QModelIndex());
	updateActions();
}

QString dateFromEpoch(int epoch)
{
	QDateTime t;
	t.setTime_t(epoch);
	return t.toString("dd/MM/yyyy");
}

void MainWindow::printDueDate()
{
	QModelIndex index = getIndex();

	v_stats->clear();

	CardInfo info = getTreeModel()->getCardInfo(index);

	if (getTreeModel()->rowCount(index) != 0)
	{
		int e = getTreeModel()->getDueDate(index);
		if (e != 0 && e != -1)
			v_stats->insertPlainText("Group due: " + dateFromEpoch(e)+"\n");
		v_stats->insertPlainText(QString("Num cards: %1\n").arg(info.m_treeItem->getNumCards()));
	}

	if (info.m_card && info.m_card->isNull() == false)
	{
		v_stats->insertPlainText(QString("Times Displayed: %1\n").arg(info.m_card->getTimesDisplayed()));
		v_stats->insertPlainText(QString("Correctly: %1\n").arg(info.m_card->getTimesAnsweredCorrectly()));
		v_stats->insertPlainText(QString("Due: %1\n").arg(dateFromEpoch(info.m_card->getDueDate())));
	}
}

QModelIndex MainWindow::getIndex() const
{
	return w_view->selectionModel()->currentIndex();
}

TreeModel* MainWindow::getTreeModel() const
{
	return dynamic_cast<TreeModel*>(w_view->model());
}

void MainWindow::on_actionPreferences_triggered()
{
	Preferences * prefs = new Preferences(this);
	prefs->setShortcuts(m_muShortcuts);
	prefs->show();
}

void MainWindow::on_actionImport_Data_triggered()
{
	QString fileName = QFileDialog::getOpenFileName(this, tr("Import Data"));
	if (fileName != "")
	{
		QModelIndex index = getIndex();
		TreeModel* model = getTreeModel();
		QFile file(fileName);
		file.open(QIODevice::ReadOnly);
		QStringList lines = QString(file.readAll()).split("\n");
		for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++)
		{
			QStringList tokens = it->split(",");
			if (tokens.size() >= 2)
			{
				int row = model->rowCount(index);
				model->insertRow(row, index);
				model->setData(model->index(row, 0, index), tokens[0].trimmed());
				if (tokens.size() == 2)
					model->setData(model->index(row, 1, index), tokens[1].trimmed());
				else
				{
					QString str("");
					for (QStringList::Iterator iit = tokens.begin(); iit != tokens.end(); iit++)
					{
						if (iit != tokens.begin())
							str += (*iit).trimmed();
					}
					model->setData(model->index(row, 1, index), str);
				}
			}
		}
	}
}


void MainWindow::beginEditFocusedArea()
{
	if (w_questionArea->hasFocus())
		enterQuestionEditMode();
	else if (w_answerArea->hasFocus())
		enterAnswerEditMode();
}

void MainWindow::endEditFocusedArea()
{
	if (!w_questionArea->isReadOnly())
		exitQuestionEditMode();
	else if (!w_answerArea->isReadOnly())
		exitAnswerEditMode();
}


void MainWindow::enterQuestionEditMode()
{
	w_answerArea->setReadOnly(true);
	w_questionArea->setReadOnly(false);
}


void MainWindow::exitQuestionEditMode()
{
	w_questionArea->setReadOnly(true);
	m_currentItemObserver->questionToModel();
}


void MainWindow::enterAnswerEditMode()
{
	w_questionArea->setReadOnly(true);
	w_answerArea->setReadOnly(false);
}


void MainWindow::exitAnswerEditMode()
{
	w_answerArea->setReadOnly(true);
	m_currentItemObserver->answerToModel();
}


void MainWindow::on_actionSave_As_triggered()
{
	getTreeModel()->saveTreeAs();
	updateActions();
}

void MainWindow::on_actionNew_triggered()
{
	getTreeModel()->createTree();
	updateActions();
}

void MainWindow::on_actionOpen_triggered()
{
	getTreeModel()->loadNewFile();
	updateActions();
}

void MainWindow::closeEvent(QCloseEvent * event)
{
	if (getTreeModel()->confirmApplicationExit())
		event->accept();
	else
		event->ignore();
}


void MainWindow::onCurrentChanged(QModelIndex,QModelIndex)
{
	printDueDate();
	updateActions();
}

void MainWindow::on_actionAbout_triggered()
{
	About* pAbout = new About();
	pAbout->show();
}
